function safelyGet(data, fieldsPath) {
  function isValidData(data) {
    return ['string', 'object'].includes(typeof data);
  }
  if (typeof fieldsPath === 'number') {
    return isValidData(data) ? data[fieldsPath] : undefined;
  }
  if (typeof fieldsPath !== 'string') {
    return undefined;
  }
  const path = fieldsPath.replace(/\[(\w+)\]/gi, ($0, $1) => {
    return '.' + $1;
  });
  const castPath = path.replace(/^\./, '').split('.');
  const length = castPath.length;
  let index = 0;
  while (isValidData(data) && index < length) {
    data = data[castPath[index++]];
  }
  return index && index === length ? data : undefined;
}

function getChildren(data, predicate) {
  if (typeof predicate === 'string') {
    return safelyGet(data, predicate);
  }
  if (typeof predicate === 'function') {
    return predicate(data);
  }
  return data;
}

function isFunc(func) {
  return typeof func === 'function';
}

/*
 * 广度优先的递归
 * * option: [Object]; { children, handle }
 * option.children: [String,Function] 可以是字符串或者函数，例如 “children”，“children.list”
 * option.handle: [Function] 递归中对数据的处理函数
 * */
function breadthFirstRecursive(data, option = {}) {
  function deep(data, options) {
    if (!data || !data.length || ['handle', 'predicate'].every((i) => !isFunc(options[i]))) {
      return;
    }
    const queue = [];
    for (const key in data) {
      const item = data[key];
      if (!item) continue;
      if (isFunc(options.predicate)) {
        if (options.predicate(item)) return item;
      }
      if (isFunc(options.handle)) {
        options.handle(item);
      }
      queue.push.apply(queue, getChildren(item, options.children));
    }
    if (queue.length) return deep(queue, options);
  }
  const opt = { children: 'children', handle: undefined, predicate: undefined };
  const options = Object.assign(opt, option);
  return deep(data, options);
}

/*
 * 深度优先的递归
 * option: [Object]; { children, handle }
 * option.children: [String,Function] 可以是字符串或者函数，例如 “children”，“children.list”
 * option.handle: [Function] 递递归中对数据的处理函数
 * */
function deepFirstRecursive(data, option = {}) {
  function deep(data, options) {
    if (!data || !data.length || ['handle', 'predicate'].every((i) => !isFunc(options[i]))) {
      return;
    }
    for (const key in data) {
      const item = data[key];
      if (!item) continue;
      if (isFunc(options.predicate)) {
        if (options.predicate(item)) return item;
      }
      if (isFunc(options.handle)) {
        options.handle(item);
      }
      const res = deep(getChildren(item, options.children), options);
      if (res) return res;
    }
  }
  const opt = { children: 'children', handle: undefined, predicate: undefined };
  const options = Object.assign(opt, option);
  return deep(data, options);
}

export default {
  breadthFirst: breadthFirstRecursive,
  deepFirst: deepFirstRecursive,
};
